package main

import (
	"errors"
	"reflect"
	"strings"
)

/* Gorm拼接sql的思路
1. 针对sql，Gorm使用分而治之的思想，将一条sql语句拆分为不同的部分，
select 【一部分】
 * 【一部分】
from * 【一部分】
where
 * 【一部分】

*/

// Expression 表达式接口
type Expression interface {
	expr()
}

// 将表达式组合的具体实现结构
type Predicate struct {
	left  Expression
	op    op // =、 < 、 > 、 AND 、 OR 、NOT
	right Expression
}

func (Predicate) expr() {}

type op string

func (o op) String() string {
	return string(o)
}

// -----------------------具体的组合形式-----------------------------

/* 针对键值匹配 */
type Column struct {
	key string
}

func (c Column) String() string {
	return c.key
}

// 实现表达式接口
func (c Column) expr() {}

func Select(name string) Column {
	return Column{
		key: name,
	}
}

type Value struct {
	value any
}

// 实现表达式接口
func (v Value) expr() {}

// 相等，k=v
// predicate与colunm都实现了expression接口，
func (c Column) Eq(v Value) Predicate {
	return Predicate{
		left:  c,
		op:    "=",
		right: v,
	}
} // 使用：Select("xxx").Eq("12")

/* 复杂表达式 */
func Not(p Predicate) Predicate {
	return Predicate{
		// left: p,
		op:    "not",
		right: p,
	}
} // 使用：Not( Select("xxx").Eq("12") )

func (f Predicate) And(p Predicate) Predicate {
	return Predicate{
		left:  f,
		op:    "and",
		right: p,
	}
} // 使用： Select("xxx").Eq("12").And( Select("yyy").Eq("13") )

// -------------------------组合成select语句--------------------------------------
type Selector[T any] struct {
	sb    strings.Builder
	tbl   string
	where []Predicate

	args []any
}

func (s *Selector[T]) buildExpression(expression Expression) error {
	switch expr := expression.(type) {
	case nil:
		return nil
	case Column:
		s.sb.WriteByte('`')
		s.sb.WriteString(expr.key)
		s.sb.WriteByte('`')
	case Value:
		s.sb.WriteByte('?')
		if s.args == nil {
			s.args = make([]any, 0, 8)
		}
		s.args = append(s.args, expr.value)
	case Predicate:
		_, ok := expr.left.(Predicate)
		if ok {
			s.sb.WriteByte('(')
		}
		if err := s.buildExpression(expr.left); err != nil {
			return err
		}
		if ok {
			s.sb.WriteByte(')')
		}

		s.sb.WriteByte(' ')
		s.sb.WriteString(expr.op.String())
		s.sb.WriteByte(' ')
		_, ok = expr.right.(Predicate)
		if ok {
			s.sb.WriteByte('(')
		}
		if err := s.buildExpression(expr.right); err != nil {
			return err
		}
		if ok {
			s.sb.WriteByte(')')
		}
	default:
		return errors.New("不支持的表达式")
	}
	return nil
}

func (s *Selector[T]) Build() (*Query, error) {
	s.sb.WriteString("SELECT * FROM ")
	if len(s.tbl) <= 0 {
		var t T
		typ := reflect.TypeOf(t)
		goName := typ.Name()
		s.sb.WriteByte('`')
		s.sb.WriteString(goName)
		s.sb.WriteByte('`')
	} else {
		s.sb.WriteString(s.tbl)
	}

	if len(s.where) > 0 {
		// 构造 WHERE
		s.sb.WriteString(" WHERE ")
		pred := s.where[0]
		for i := 1; i < len(s.where); i++ {
			pred = pred.And(s.where[i])
		}
		err := s.buildExpression(pred)
		if err != nil {
			return nil, err
		}
	}

	s.sb.WriteByte(';')
	return &Query{
		SQL:  s.sb.String(),
		Args: s.args,
	}, nil
}

func NewSelect[T any]() *Selector[T] {
	return &Selector[T]{}
}

type Query struct {
	SQL  string
	Args []any
}
